package darlingbastard.stacks;

import org.apache.taglibs.standard.tag.common.core.SetSupport;
import org.apache.taglibs.standard.tag.common.core.NullAttributeException;
import org.apache.taglibs.standard.tag.el.core.ExpressionUtil;
import org.apache.taglibs.standard.resources.Resources;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import java.beans.PropertyDescriptor;
import java.beans.Introspector;
import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by bishope
 * Date: Mar 11, 2006
 */
public class SetTag extends SetSupport {

    //*********************************************************************
    // 'Private' state (implementation details)

    private String value_;			// stores EL-based property
    private String target_;			// stores EL-based property
    private String property_;			// stores EL-based property


    //*********************************************************************
    // Constructor

    public SetTag() {
        super();
        init();
    }


    //*********************************************************************
    // Tag logic

    // evaluates expression and chains to parent
    public int doStartTag() throws JspException {

        // evaluate any expressions we were passed, once per invocation
        evaluateExpressions();

        // chain to the parent implementation
        return super.doStartTag();
    }


    // Releases any resources we may have (or inherit)
    public void release() {
        super.release();
        init();
    }


    //*********************************************************************
    // Accessor methods

    public void setValue(String value_) {
        this.value_ = value_;
        this.valueSpecified = true;
    }

    public void setTarget(String target_) {
        this.target_ = target_;
    }

    public void setProperty(String property_) {
        this.property_ = property_;
    }


    //*********************************************************************
    // Private (utility) methods

    // (re)initializes state (during release() or construction)
    private void init() {
        // null implies "no expression"
        value_ = target_ = property_ = null;
    }

    /* Evaluates expressions as necessary */
    private void evaluateExpressions() throws JspException {
        /*
         * Note: we don't check for type mismatches here; we assume
         * the expression evaluator will return the expected type
         * (by virtue of knowledge we give it about what that type is).
         * A ClassCastException here is truly unexpected, so we let it
         * propagate up.
         */

        setScope("request");

        String value_ = var;

        System.out.println("SetTag.evaluateExpressions: var=" + var);
        String[] strings = var.split("\\.");
        target = getOrCreateTarget(strings);
        if (target != null) {
            property = strings[strings.length - 1];
            setVar(null);
            System.out.println("" + target + "." + property + " = ???");
        }

        // 'value'
        try {
            System.out.println("checking existing value from ${" + value_ + "}");
            value = ExpressionUtil.evalNotNull("set", "value", "${" + value_ + "}", Object.class, this, pageContext);
        } catch (NullAttributeException ex) {
            System.out.println("value was null");
            // explicitly let 'value' be null
            value = null;
        }

        // 'target'
//        target = ExpressionUtil.evalNotNull("set", "target", target_, Object.class, this, pageContext);

        // 'property'
        if (false) {
        try {
            property = (String) ExpressionUtil.evalNotNull("set", "property", property_, String.class, this, pageContext);
        } catch (NullAttributeException ex) {
// explicitly let 'property' be null
            property = null;
        }
        }
    }

    private Object getOrCreateTarget(String[] path) {
        Object next = null;
        for (int i = 0; i < path.length - 1; i++) {
            String attributeName = path[i];
            Object target = next;
            next = getAttribute(target, attributeName);
            if (next != null) continue;
            if (i < path.length - 1) {
                if (target == null) {
                    System.out.println("creating target " + attributeName);
                    next = setAttribute(target, attributeName, new TreeMap());
                } else if (target instanceof Map) {
                    System.out.println("creating target " + attributeName);
                    next = setAttribute(target, attributeName, new TreeMap());
                }
            }
        }
        return next;
    }

    private Object setAttribute(Object target, String name, Object value) {
        if (target == null) pageContext.setAttribute(name, value, PageContext.REQUEST_SCOPE);
        if (target instanceof Map) ((Map)target).put(name, value);
        return value;
    }

    private Object getAttribute(Object target, String name) {
        System.out.println("SetTag.getAttribute target=" + target + ", name=" + name);
        if (target == null) return pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
        if (target instanceof Map) return ((Map)target).get(name);
        return null;
    }


    private String var = null;

    // for tag attribute
    public void setVar(String var) {
        this.var = var;
        super.setVar(var);
    }

    public String getVar() {
        return this.var;
    }

    private String superKey() {
        return "_s_super." + var + "_s_";
    }

    public int doEndTag() throws JspException {
        if (value != null && bodyContent != null) {
            String sValue = String.valueOf(value);
            if (sValue.indexOf(superKey()) >= 0) {
                // *** replace all instances of
                value = sValue.replaceAll(superKey(), bodyContent.getString().trim());
            }
        }
        return super.doEndTag();
    }


}
